﻿using System;
using System.Collections.Generic;
using System.Linq;
using Fuse8.DomainFramework.Common.Locator;
using Fuse8.DomainFramework.DataAccess;
using Fuse8.DomainFramework.DataAccess.SqlDatabase;
using Fuse8.DomainFramework.Domain;
using Fuse8.DomainFramework.Practices.DataAccess.SqlRepository;
using Fuse8.DomainFramework.Practices.Domain.Interfaces;
using Fuse8.DomainFramework.Practices.SystemFacade;

namespace Fuse8.DomainFramework.Practices.DataAccess.Commands
{
    public class SqlBaseCommand<TDomain, TParameter, TKey> : IEntityDataCommand<TDomain, TParameter, TKey>
        where TDomain : class, IEntityObject<TKey>, new()
        where TParameter : ICommandParameter where TKey : IComparable<TKey>, IEquatable<TKey>
    {
        public virtual IQueryable<TDomain> Execute()
        {
            var currentUnitOfWork = GetCurrentUnitOfWork();
            IEnumerable<TDomain> result = null;

			var mapper = GetMapper() as SqlBaseMapper<TDomain, TKey>;
			if (mapper != null && mapper.IsMultiPropertiesAssigned<TParameter>())
			{
				var listSqlCommand = new GenericListSqlCommand<TDomain, TKey, TParameter> {Parameter = Parameter};
				var container = listSqlCommand.ExecuteOne();
			    return container != null ? container.List.AsQueryable() : new List<TDomain>().AsQueryable();
			}

            if (currentUnitOfWork != null)
            {
                result = SqlUtility<TDomain, TKey>.ExecuteReader(currentUnitOfWork.Connection,
                                                                currentUnitOfWork.Transaction,
                                                                CommandName, Parameter,
																GetMapper());
            }

            return (result ?? Enumerable.Empty<TDomain>()).AsQueryable();

        }

        protected virtual SqlUnitOfWork<TDomain> GetCurrentUnitOfWork()
        {
            var factory = DependencyServiceLocator.Current.GetInstance<IUnitOfWorkFactory<TDomain>>();
            return factory.Get(false) as SqlUnitOfWork<TDomain>;
        }

        protected virtual ISqlEntityDataMapper<TDomain, TKey> GetMapper()
        {
            return DependencyServiceLocator.Current.GetInstance<ISqlEntityDataMapper<TDomain, TKey>>();
        }

        public virtual TDomain ExecuteOne()
        {
        	var mapper = GetMapper() as SqlBaseMapper<TDomain, TKey>;
			if (mapper != null && mapper.IsMultiPropertiesAssigned<TParameter>())
			{
				var unitOfWork = GetCurrentUnitOfWork();
				return unitOfWork != null ? SqlUtility<TDomain, TKey>.ExecuteMultiReader(unitOfWork.Connection, unitOfWork.Transaction, CommandName, Parameter, mapper) : null;				
			}
            return Execute().FirstOrDefault();
        }

    	public virtual string CommandName
    	{
			get { return CommandNameFacade.GetCommandName<TDomain, TParameter>(); }
    	}

    	public virtual string CacheKey
        {
            get { return Parameter.CacheKey(); }
        }

        public TParameter Parameter { get; set; }

    }
}